<?php
// +----------------------------------------------------------------------
// | Copyright (c) 2020 All rights reserved.
// +----------------------------------------------------------------------
// | User: Simony <1362229026@qq.com>
// | wechat: Shen86579595
// +----------------------------------------------------------------------

namespace app\common\logic;


class AppWxPayLogic
{

    public $appid = ""; //应用ID
    public $mch_id = ""; //商户号
    public $notify_url;
    public $key = ""; //key为商户平台设置的密钥key
    public $trade_type = 'APP';

    public $common_param = [];

    public function __construct()
    {
        $weChatPay = sysConfig('wechat_pay');
        $this->appid = isset($weChatPay['appid']) ? $weChatPay['appid'] : '';
        $this->mch_id = isset($weChatPay['mch_id']) ? $weChatPay['mch_id'] : '';
        $this->key = isset($weChatPay['key']) ? $weChatPay['key'] : '';
        $this->notify_url = config('app.wechat.pay.notify_url');

        //设置公共参数
        $param['appid'] = $this->appid;
        $param['mch_id'] = $this->mch_id;

        $this->common_param = $param;
    }

    /**
     * User: Simony
     * Function:
     * @param $order_id
     * @param $goods_name
     * @param $price
     * @param int $attach 1充值 2
     * @return array
     */
    public function unifiedorder($out_trade_no, $goods_name, $price, $attach = 1)
    {
        //公共参数
        $param = $this->common_param; // appid  && mch_id && nonce_str
        //非公共参数
        $param['nonce_str'] = $this->getNonceStr();
        $param['body'] = $goods_name;
        $param['spbill_create_ip'] = request()->ip();
        $param['total_fee'] = $price * 100;  //总金额
        $param['out_trade_no'] = $out_trade_no;
        $param['notify_url'] = $this->notify_url;
        $param['trade_type'] = $this->trade_type;      //将参与签名的数据保存到数组
        $param['attach'] = $attach;
        $param['sign'] = $this->getSign($param);

        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        $xml_data = http_request($url,'POST',$this->arrToXml($param));
        $data = $this->xmlToArr($xml_data);

        if ($data['return_code'] == 'FAIL') return ds_arr(ERROR, '签名失败：' . $data['return_msg']); // 如果微信返回错误码为FAIL，则代表请求失败，返回失败信息；

        //如果上一次请求成功，那么我们将返回的数据重新拼装，进行第二次签名
        $config = array(
            'appid' => $data['appid'],
            'partnerid' => $data['mch_id'],
            'prepayid' => $data['prepay_id'],
            'noncestr' => $data['nonce_str'],
            'timestamp' => time(),
            'package' => 'Sign=WXPay'
        );

        //二次签名；
        $config['sign'] = $this->getSign($config);
        $config['pay_desc'] = '微信支付';
        $config['order_sn'] = $out_trade_no;
        $config['payment_price'] = $price;

        return ds_arr(SUCCESS, '成功', $config);


    }


    /**
     * User: Simony
     * Function: 查询订单状态
     * @param $out_trade_no
     * @return mixed
     */
    public function queryOrder($out_trade_no)
    {
        //公共参数
        $param = $this->common_param; // appid  && mch_id && nonce_str
        //非公共参数
        $param['nonce_str'] = $this->getNonceStr();
        $param['out_trade_no'] = $out_trade_no;
        $param['sign'] = $this->getSign($param);

        $url = "https://api.mch.weixin.qq.com/pay/orderquery";
        $xml_data = http_request($url,'POST',$this->arrToXml($param));

        $data = $this->xmlToArr($xml_data);
        if ($data['return_code'] == 'FAIL') return ds_arr(ERROR, '签名失败：' . $data['return_msg']);

        if ($data['result_code'] == 'FAIL') return ds_arr(ERROR, $data['err_code_des']);

        $res = [
            'total_fee' => floatval($data['total_fee'] / 100),
            'out_trade_no' => $data['out_trade_no'],
            'trade_state' => $data['trade_state'],
            'trade_state_desc' => $data['trade_state_desc'],
        ];
        return ds_arr(SUCCESS, '查询成功', $res);
    }

    /**
     * User: Simony
     * Function:回调校验
     * @param $data
     * @param $sign
     * @return bool
     */
    public function notifyCheck($data)
    {
        $sign = $data['sign'];
        unset($data['sign']);
        $compare_sign = $this->getSign($data);
        return $compare_sign == $sign ? true : false;
    }

    /************************************************************************************************************************/




    /**
     * User: Simony
     * Function:生成随机数并返回
     * @return string
     */
    public function getNonceStr()
    {
        $str = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';//62个字符
        $str = str_shuffle($str);
        $str = substr($str, 0, 32);
        return $str;
    }


    /**
     * User: Simony
     * Function:获取参数签名
     * @param  $params  要传递的参数数组
     * @return String
     */
    private function getSign($params)
    {

        ksort($params);        //将参数数组按照参数名ASCII码从小到大排序

        foreach ($params as $key => $item) {
            if (!empty($item)) {         //剔除参数值为空的参数
                $newArr[] = $key . '=' . $item;     // 整合新的参数数组
            }
        }
        $stringA = implode("&", $newArr);         //使用 & 符号连接参数
        $stringSignTemp = $stringA . "&key=" . $this->key;        //拼接key
        $stringSignTemp = MD5($stringSignTemp);       //将字符串进行MD5加密
        $sign = strtoupper($stringSignTemp);      //将所有字符转换为大写
        return $sign;
    }


    /**
     * User: Simony
     * Function:为微信官方返回的数据类型做准备
     * @param $params
     * @return string
     */
    public function arrToString($params)
    {

        ksort($params);        //将参数数组按照参数名ASCII码从小到大排序

        foreach ($params as $key => $item) {
            if (!empty($item)) {         //剔除参数值为空的参数
                $newArr[] = $key . '=' . $item;     // 整合新的参数数组
            }
        }
        $stringA = implode("&", $newArr);
        return $stringA;
    }


    /**
     * User: Simony
     * Function: 设置回调地址
     * @param $url
     */
    public function setNotifyUrl($url)
    {
        if (is_string($url)) {
            $this->notify_url = $url;
        }
    }


    /**
     * User: Simony
     * Function: 获取回调参数
     * @return bool|mixed|\SimpleXMLElement
     */
    public function getNotifyData()
    {
        $postXml = $GLOBALS["HTTP_RAW_POST_DATA"];    // 接受通知参数；
        if (empty($postXml)) {
            return false;
        }
        $postObj = $this->xmlToObject($postXml);      // 调用解析方法，将xml数据解析成对象
        if ($postObj === false) {
            return false;
        }
        if (!empty($postObj->return_code)) {
            if ($postObj->return_code == 'FAIL') {
                return false;
            }
        }
        return $postObj;          // 返回结果对象；
    }


    /**
     * User: Simony
     * Function: xml转数组
     * @param $xmlStr
     * @return bool|mixed|\SimpleXMLElement
     */
    public function xmlToArr($xmlStr)
    {
        if (!is_string($xmlStr) || empty($xmlStr)) {
            return false;
        }
        // 由于解析xml的时候，即使被解析的变量为空，依然不会报错，会返回一个空的对象，所以，我们这里做了处理，当被解析的变量不是字符串，或者该变量为空，直接返回false
        $postObj = simplexml_load_string($xmlStr, 'SimpleXMLElement', LIBXML_NOCDATA);
        $data = json_decode(json_encode($postObj), true);
        //将xml数据转换成数组
        return $data;
    }

    /**
     * User: Simony
     * Function: 数组转xml
     * @param array $data
     * @return string
     */
    public function arrToXml($data = array())
    {
        if (!is_array($data) || count($data) <= 0) {
            return '数组异常';
        }
        $xml = "<xml>";
        foreach ($data as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }
}
